#include "dlgpcaphub.h"
#include "./ui_dlgpcaphub.h"
#include "io/pcapio.h"
#include <QDateTime>
#include <QSettings>

static const int VIEWCOL_ID = 0 ;
static const int VIEWCOL_NAME = 1 ;
static const int VIEWCOL_SWITCH = 2 ;
static const int VIEWCOL_FILTER = 3 ;
static const int VIEWCOL_MACS = 4 ;

DlgPCAPHub::DlgPCAPHub(TASKBUS::cmdlineParser & cmd,QWidget *parent)
	: QDialog(parent)
	, ui(new Ui::DlgPCAPHub)
	, m_pInterfaceMod(new QStandardItemModel(this))
	, m_pMsgMod(new QStandardItemModel(this))
	, m_tunnelThread(new QThread(this))
	, m_cmdline(cmd)
{
	// 初始化UI
	ui->setupUi(this);
	// 设置表格列数
	m_pInterfaceMod->setColumnCount(5);
	// 设置表格表头
	m_pInterfaceMod->setHeaderData(VIEWCOL_ID,Qt::Horizontal,"ID");
	m_pInterfaceMod->setHeaderData(VIEWCOL_NAME,Qt::Horizontal,tr("Interface"));
	m_pInterfaceMod->setHeaderData(VIEWCOL_FILTER,Qt::Horizontal,tr("Filter"));
	m_pInterfaceMod->setHeaderData(VIEWCOL_SWITCH,Qt::Horizontal,tr("Switch mod"));
	m_pInterfaceMod->setHeaderData(VIEWCOL_MACS,Qt::Horizontal,tr("MACs"));

	// 设置表格模型
	ui->tableView_interfaces->setModel(m_pInterfaceMod);
	// 设置表格列宽
	ui->tableView_interfaces->setColumnWidth(VIEWCOL_ID,72);
	ui->tableView_interfaces->setColumnWidth(VIEWCOL_NAME,384);
	ui->tableView_interfaces->setColumnWidth(VIEWCOL_FILTER,256);
	ui->tableView_interfaces->setColumnWidth(VIEWCOL_SWITCH,84);
	ui->tableView_interfaces->setColumnWidth(VIEWCOL_MACS,512);
	// 隐藏ID列
	ui->tableView_interfaces->hideColumn(VIEWCOL_ID);
	// 设置消息列表模型
	ui->listView_msgs->setModel(m_pMsgMod);

	// 枚举接口
	enumInterfaces();
	// 发送消息
	slot_msg("System Started.");
	// 连接信号和槽
	connect (this, &DlgPCAPHub::sig_msg, this, &DlgPCAPHub::slot_msg,Qt::QueuedConnection);
	// 设置窗口标志
	setWindowFlag(Qt::WindowMinMaxButtonsHint,true);
	// 启动定时器
	m_nTimerID = startTimer(1000);

	// 获取命令行参数
	m_nTBIns = m_cmdline.toInt("instance",0);
	// 加载设置
	loadSettings();
	//TCP Tunnel
	m_tunnel = new tcpTunnel;
	m_tunnel->moveToThread(m_tunnelThread);
	// 连接信号和槽
	connect(this,&DlgPCAPHub::sig_start_tunnel, m_tunnel, &tcpTunnel::startWork,Qt::QueuedConnection);
	connect(this,&DlgPCAPHub::sig_stop_tunnel, m_tunnel, &tcpTunnel::stopWork,Qt::QueuedConnection);
	connect(m_tunnel, &tcpTunnel::sig_message,this,&DlgPCAPHub::slot_msg,Qt::QueuedConnection);
	// 启动线程
	m_tunnelThread->start();

	//TaskBus
	if (m_cmdline.contains("function"))
	{
		// 创建线程
		m_pStdinThread = new listen_thread(&m_cmdline,this);
		m_pStdoutThread = new cap_thread(this);
		// 连接信号和槽
		connect (m_pStdinThread,&listen_thread::quit_app,this,&DlgPCAPHub::close,Qt::QueuedConnection);
		connect (m_pStdinThread,&listen_thread::sig_message,this,&DlgPCAPHub::slot_msg,Qt::QueuedConnection);
		// 启动线程
		m_pStdinThread->start();
		// 设置线程运行函数
		m_pStdoutThread->setRunner(std::bind(stdoutLoop,&m_cmdline,[this](QString m){
			emit sig_msg(m);
		}));
		// 启动线程
		m_pStdoutThread->start();
		// 判断是否隐藏窗口
		if (m_cmdline.toInt("hide",0)>0)
		{
			// 启动定时器
			m_nAutoStartID = startTimer(100);
			m_nStartCounter = 1;
			// 隐藏窗口
			hide();
		}
		// 判断是否自动启动
		else if (m_cmdline.toInt("autostart",0)>0)
		{
			// 启动定时器
			m_nAutoStartID = startTimer(100);
			m_nStartCounter = 1;
		}
	}
	// 判断是否自动启动
	else if (ui->checkBox_autoStart->isChecked())
	{
		// 启动定时器
		m_nAutoStartID = startTimer(1000);
		m_nStartCounter = 10;
	}
}

DlgPCAPHub::~DlgPCAPHub()
{
	saveSettings();
	PCAPIO::pcap_stop = true;
	//Stop TaskBus
	if (m_pStdinThread)
	{
		m_pStdinThread->terminate();
		m_pStdinThread->wait();
		m_pStdinThread->deleteLater();
		m_pStdinThread = nullptr;
		m_pStdoutThread->terminate();
		m_pStdoutThread->wait();
		m_pStdoutThread->deleteLater();
		m_pStdoutThread = nullptr;
	}
	//Stop cap threads
	const int threads = m_threads.size();
	for (int i=0;i<threads;++i)
	{
		m_threads[i]->wait(1000);
		m_threads[i]->terminate();
		m_threads[i]->deleteLater();
	}
	m_threads.clear();
	m_tunnelThread->quit();
	m_tunnelThread->wait(2000);
	m_tunnelThread->terminate();
	m_tunnelThread->deleteLater();
	delete ui;
}

void DlgPCAPHub::timerEvent(QTimerEvent * evt)
{
	if (evt->timerId()==m_nTimerID)
	{
		ui->lcdNumber_packs->display(QString("%1").arg(PCAPIO::pcap_recv_pos));
		QMap<quint64, PCAPIO::tag_portAssign> ports = PCAPIO::macPorts();
		QMap<QString, QList<quint64> > macs;
		for (auto p = ports.begin();p!=ports.end();++p)
		{
			quint64 mac = p.key();
			PCAPIO::tag_portAssign & ass = p.value();
			macs[ass.portName].push_back(mac);
		}
		const int ethers = m_pInterfaceMod->rowCount();
		for (int i=0;i<ethers;++i)
		{
			m_pInterfaceMod->setData(
						m_pInterfaceMod->index(i,VIEWCOL_MACS),""
						);
		}
		for (auto p = macs.begin();p!=macs.end();++p)
		{
			QString pn = p.key();
			QString v;
			foreach(quint64 mac, p.value())
			{
				v += QString().asprintf("%012llX, ",mac);
			}
			if (m_devRows.contains(pn))
			{
				int pi = m_devRows[pn];
				m_pInterfaceMod->setData(
							m_pInterfaceMod->index(pi,VIEWCOL_MACS),v
							);
			}
			else
			{
				ui->plainTextEdit_remote->setPlainText(pn+":"+v+"\n");
			}
		}
	}
	else if (evt->timerId()==m_nAutoStartID)
	{
		slot_msg(tr("Auto start in %1 seconds.").arg(m_nStartCounter));
		if (m_nStartCounter>0)
			--m_nStartCounter;
		if (m_nStartCounter==0)
		{
			killTimer(m_nAutoStartID);
			m_nStartCounter = -1;
			m_nAutoStartID = -1;
			on_pushButton_startEnd_clicked();
		}
	}
}

void DlgPCAPHub::enumInterfaces()
{
	m_devs.clear();
	m_pInterfaceMod->removeRows(0,m_pInterfaceMod->rowCount());
	QString err = PCAPIO::pcapio_interfaces(m_devs);
	if (err.length())
	{
		slot_msg(err);
	}
	int i = 0;
	for (auto p =m_devs.begin();p!=m_devs.end();++p)
	{
		QString name = p.key();
		QString detailed = p.value();
		//column 0 is hidden for editers
		m_pInterfaceMod->appendRow(new QStandardItem(name));
		m_pInterfaceMod->setData(
					m_pInterfaceMod->index(i,VIEWCOL_NAME),
					detailed);
		m_pInterfaceMod->setData(
					m_pInterfaceMod->index(i,VIEWCOL_NAME),
					false,
					Qt::CheckStateRole
					);
		m_pInterfaceMod->setData(
					m_pInterfaceMod->index(i,VIEWCOL_NAME),
					detailed,
					Qt::ToolTipRole
					);
		m_devRows[name] = i;
		++i;
	}

}
void DlgPCAPHub::slot_msg(QString msg)
{
	QDateTime dtm = QDateTime::currentDateTime();
	QString dtmStr = dtm.toString("yyyy-MM-dd HH:mm:ss.zzz");
	m_pMsgMod->appendRow(new QStandardItem(dtmStr+">"+msg.trimmed()));
	if (m_pMsgMod->rowCount()>1024)
		m_pMsgMod->removeRows(0,m_pMsgMod->rowCount()-1024);
	ui->listView_msgs->scrollToBottom();
	//QCoreApplication::processEvents();
}


void DlgPCAPHub::on_tableView_interfaces_clicked(const QModelIndex &index)
{
	if (index.row()>=0 && index.row()<m_devs.size() && index.column()==VIEWCOL_NAME)
	{
		QString name = m_pInterfaceMod->data(
					m_pInterfaceMod->index(index.row(),VIEWCOL_ID)
					).toString();
		if (m_devs.contains(name))
		{
			bool chk = m_pInterfaceMod->data(
						m_pInterfaceMod->index(index.row(),VIEWCOL_NAME)
						,Qt::CheckStateRole
						).toBool();
			m_pInterfaceMod->setData(
						m_pInterfaceMod->index(index.row(),VIEWCOL_NAME),
						!chk,
						Qt::CheckStateRole
						);
		}
	}
	else if (index.row()>=0 && index.row()<m_devs.size() && index.column()==VIEWCOL_SWITCH)
	{
		QString name = m_pInterfaceMod->data(
					m_pInterfaceMod->index(index.row(),VIEWCOL_ID)
					).toString();
		if (m_devs.contains(name))
		{
			bool switchmod = m_pInterfaceMod->data(
						m_pInterfaceMod->index(index.row(),VIEWCOL_SWITCH)
						,Qt::CheckStateRole
						).toBool();
			m_pInterfaceMod->setData(
						m_pInterfaceMod->index(index.row(),VIEWCOL_SWITCH),
						!switchmod,
						Qt::CheckStateRole
						);
			m_pInterfaceMod->setData(
						m_pInterfaceMod->index(index.row(),VIEWCOL_SWITCH),
						!switchmod?tr("SWITCH"):tr("HUB"));
		}

	}
}

/*!
 * \brief DlgPCAPHub::on_pushButton_startEnd_clicked
 * start or stop the pcapio
*/
void DlgPCAPHub::on_pushButton_startEnd_clicked()
{
	// 如果自动启动ID大于等于0，则停止定时器，并将自动启动ID和启动计数器重置为-1
	if (m_nAutoStartID >=0)
	{
		killTimer(m_nAutoStartID);
		m_nAutoStartID = -1;
		m_nStartCounter = -1;
	}
	// 如果线程为空，则保存设置，重置PCAPIO，获取接口数量
	if (m_threads.isEmpty())
	{
		saveSettings();
		PCAPIO::reset();
		const int ethers = m_pInterfaceMod->rowCount();
		int id = 0;
		// 获取监听端口
		const int port = ui->lineEdit_listenerPort->text().toInt();
		// 遍历接口
		for (int i=0;i<ethers;++i)
		{
			// 获取接口名称的选中状态
			bool chk = m_pInterfaceMod->data(
						m_pInterfaceMod->index(i,VIEWCOL_NAME)
						,Qt::CheckStateRole
						).toBool();
			// 如果未选中，则跳过
			if (!chk)
				continue;
			const bool switchmod = m_pInterfaceMod->data(
						m_pInterfaceMod->index(i,VIEWCOL_SWITCH)
						,Qt::CheckStateRole
						).toBool();
			const QString strName =  m_pInterfaceMod->data(
						m_pInterfaceMod->index(i,VIEWCOL_ID)).toString();
			const QString filter =  m_pInterfaceMod->data(
						m_pInterfaceMod->index(i,VIEWCOL_FILTER)).toString();
			cap_thread * recv = new cap_thread(this);
			recv->setRunner(std::bind(
								PCAPIO::recv_loop,strName,filter,id,port,
								[this](QString msg)->void{
								emit sig_msg(msg);
							}));

			m_threads.push_back(recv);
			cap_thread * send = new cap_thread(this);
			send->setRunner(std::bind(
								PCAPIO::send_loop,strName,id,switchmod,
								[this](QString msg)->void{
								emit sig_msg(msg);
							}));

			m_threads.push_back(send);

			++id;
		}
		const int threads = m_threads.size();
		for (int i=0;i<threads;++i)
			m_threads[i]->start();
		if (port > 0 && port < 65536)
			emit sig_start_tunnel(
					ui->lineEdit_listenerAddr->text(),
					ui->lineEdit_listenerPort->text(),
					ui->checkBox_listen->isChecked(),
					ui->checkBox_ssl->isChecked());
	}
	else
	{
		PCAPIO::pcap_stop = true;
		const int threads = m_threads.size();
		for (int i=0;i<threads;++i)
		{
			m_threads[i]->wait(1000);
			m_threads[i]->terminate();
			m_threads[i]->deleteLater();
		}
		m_threads.clear();
		emit sig_stop_tunnel();
	}
	if (m_threads.size())
		ui->pushButton_startEnd->setText(tr("Stop"));
	else
		ui->pushButton_startEnd->setText(tr("Start"));
}

void DlgPCAPHub::saveSettings()
{
	QSettings settings(QCoreApplication::applicationFilePath()+QString("%1.ini").arg(m_nTBIns?QString(".TBINS%1").arg(m_nTBIns):QString(""))
					   ,QSettings::IniFormat);
	const int ethers = m_pInterfaceMod->rowCount();
	for (int i=0;i<ethers;++i)
	{
		QString strDevID =  m_pInterfaceMod->data(
					m_pInterfaceMod->index(i,VIEWCOL_ID)).toString();
		QString filter =  m_pInterfaceMod->data(
					m_pInterfaceMod->index(i,VIEWCOL_FILTER)).toString();
		bool chk = m_pInterfaceMod->data(
					m_pInterfaceMod->index(i,VIEWCOL_NAME)
					,Qt::CheckStateRole
					).toBool();
		bool switchmod = m_pInterfaceMod->data(
					m_pInterfaceMod->index(i,VIEWCOL_SWITCH)
					,Qt::CheckStateRole
					).toBool();

		QString key1 = "ether_hex/"+strDevID.toLocal8Bit().toHex();
		QString key2 = "ether_chk/"+strDevID.toLocal8Bit().toHex();
		QString key3 = "ether_switch/"+strDevID.toLocal8Bit().toHex();

		settings.setValue(key1,filter);
		settings.setValue(key2,chk);
		settings.setValue(key3,switchmod);
	}
	settings.setValue("ui/lineEdit_listenerAddr",ui->lineEdit_listenerAddr->text());
	settings.setValue("ui/lineEdit_listenerPort",ui->lineEdit_listenerPort->text());
	settings.setValue("ui/checkBox_listen",ui->checkBox_listen->isChecked());
	settings.setValue("ui/checkBox_ssl",ui->checkBox_ssl->isChecked());
	settings.setValue("ui/checkBox_autoStart",ui->checkBox_autoStart->isChecked());
}
void DlgPCAPHub::loadSettings()
{
	QSettings settings(QCoreApplication::applicationFilePath()+QString("%1.ini").arg(m_nTBIns?QString(".TBINS%1").arg(m_nTBIns):QString(""))
					   ,QSettings::IniFormat);
	const int ethers = m_pInterfaceMod->rowCount();
	for (int i=0;i<ethers;++i)
	{
		QString strDevID =  m_pInterfaceMod->data(
					m_pInterfaceMod->index(i,VIEWCOL_ID)).toString();

		QString filter =  m_pInterfaceMod->data(
					m_pInterfaceMod->index(i,VIEWCOL_FILTER)).toString();

		bool chk = m_pInterfaceMod->data(
					m_pInterfaceMod->index(i,VIEWCOL_NAME)
					,Qt::CheckStateRole
					).toBool();
		bool switchmod = m_pInterfaceMod->data(
					m_pInterfaceMod->index(i,VIEWCOL_SWITCH)
					,Qt::CheckStateRole
					).toBool();

		QString key1 = "ether_hex/"+strDevID.toLocal8Bit().toHex();
		QString key2 = "ether_chk/"+strDevID.toLocal8Bit().toHex();
		QString key3 = "ether_switch/"+strDevID.toLocal8Bit().toHex();

		filter = settings.value(key1,filter).toString();
		m_pInterfaceMod->setData(
					m_pInterfaceMod->index(i,VIEWCOL_FILTER),filter);

		chk = settings.value(key2,chk).toBool();

		m_pInterfaceMod->setData(
					m_pInterfaceMod->index(i,VIEWCOL_NAME),chk,Qt::CheckStateRole);

		switchmod = settings.value(key3,switchmod).toBool();

		m_pInterfaceMod->setData(
					m_pInterfaceMod->index(i,VIEWCOL_SWITCH),switchmod,Qt::CheckStateRole);
		m_pInterfaceMod->setData(
					m_pInterfaceMod->index(i,VIEWCOL_SWITCH),switchmod?tr("SWITCH"):tr("HUB"));

	}
	ui->lineEdit_listenerAddr->setText(settings.value("ui/lineEdit_listenerAddr",ui->lineEdit_listenerAddr->text()).toString());
	ui->lineEdit_listenerPort->setText(settings.value("ui/lineEdit_listenerPort",ui->lineEdit_listenerPort->text()).toString());
	ui->checkBox_listen->setChecked(settings.value("ui/checkBox_listen",ui->checkBox_listen->isChecked()).toBool());
	ui->checkBox_ssl->setChecked(settings.value("ui/checkBox_ssl",ui->checkBox_ssl->isChecked()).toBool());
	ui->checkBox_autoStart->setChecked(settings.value("ui/checkBox_autoStart",ui->checkBox_autoStart->isChecked()).toBool());
}

void DlgPCAPHub::on_checkBox_autoStart_clicked()
{
	saveSettings();
	if (m_nAutoStartID >=0)
	{
		killTimer(m_nAutoStartID);
		m_nAutoStartID = -1;
		m_nStartCounter = -1;
	}
}

